/*
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "modelmanager.h"
#include "modelmanager_p.h"
#include <QStack>
#include <QThread>
#include <QGuiApplication>
#include <QMetaEnum>
#include <QDebug>
#include <KWindowSystem>
#include "base/categoryitem.h"
#include "core/desktopdatahandler.h"
#include "core/appmanager.h"
#include "core/configmanager.h"
#include "taskbarmanager.h"
#include "appcategorymanager.h"

ModelManager::ModelManager(QObject *parent)
    : QObject(parent)
    , d_ptr(new ModelManagerPrivate(this))
{
    qRegisterMetaType<MultiPageModel>("MultiPageModel");
}

ModelManager::~ModelManager()
{
    delete d_ptr;
}

QAbstractListModel *ModelManager::launcherModel() const
{
    Q_D(const ModelManager);
    return const_cast<MultiPageModel *>(&d->launcherModel);
}

QAbstractListModel *ModelManager::groupModel(const quint32 &id) const
{
    Q_D(const ModelManager);
    if (!d->groupModel.contains(id)) {
        return nullptr;
    }
    return d->groupModel.value(id);
}

QAbstractListModel *ModelManager::categoryModel() const
{
    Q_D(const ModelManager);
    return const_cast<CategoryModel *>(&d->categoryModel);
}

QAbstractListModel *ModelManager::categoryItemsModel() const
{
    Q_D(const ModelManager);
    return const_cast<MultiPageModel *>(&d->categoryItemsModel);
}

int ModelManager::groupPageItemCount(int groupId, int pageIndex)
{
    Q_D(ModelManager);
    if (groupId > 0 && !d->groupModel.contains(groupId)) {
        return 0;
    }
    MultiPageModel *model = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
    if (pageIndex < 0 || pageIndex > model->count()) {
        return 0;
    }
    return model->isEmpty() ? 0 : model->at(pageIndex)->count();
}

int ModelManager::groupPageCount(int groupId)
{
    Q_D(ModelManager);
    if (!d->groupModel.contains(groupId)) {
        return 0;
    }
    return d->groupModel.value(groupId)->count();
}

void ModelManager::moveItem(int from,
                            int to,
                            int page,
                            int groupId)
{
    Q_D(ModelManager);
    if (from == to) {
        return;
    }

    MultiPageModel *model = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
    model->at(page)->move(from, to);
}

void ModelManager::moveItemToGroup(int fromIndex,
                                   int fromPage,
                                   int fromGroupId,
                                   int toGroupId)
{
    Q_D(ModelManager);
    Q_ASSERT(toGroupId >= 0);
    if (!d->groupModel.contains(toGroupId)) {
        qWarning() << "Move item to group failed" << "toGroup id: " << toGroupId << ".";
        return;
    }
    if (fromGroupId == -1 && !d->itemIndexIsValid(fromIndex, fromPage, &d->launcherModel)) {
        qWarning() << "Move item to group failed, from index: "
                   << fromIndex << ","
                   << "from page: " << fromPage << ","
                   << "fromGroup id: " << fromGroupId << ".";
        return;
    }
    if (fromGroupId >= 0) {
        if (!d->groupModel.contains(fromGroupId) ||
            !d->itemIndexIsValid(fromIndex, fromPage, d->groupModel.value(fromGroupId))) {
            qWarning() << "Move item to group failed, from index: "
                       << fromIndex << ","
                       << "from page: " << fromPage << ","
                       << "fromGroup id: " << fromGroupId << ".";
            return;
        }
    }
    MultiPageModel *model = d->groupModel.value(toGroupId);
    LauncherItem* moveItem = nullptr;
    if (fromGroupId >= 0) {
        moveItem = d->groupModel.value(fromGroupId)->at(fromPage)->takeAt(fromIndex);
    } else {
        moveItem = d->launcherModel.at(fromPage)->takeAt(fromIndex);
    }

    if (d->placeholderAvailable() && d->placeholderItemInfo.groupId != toGroupId) {
        for (int pageIndex = 0; pageIndex < model->count(); ++pageIndex) {
            if (model->at(pageIndex)->count() < d->maxItemCountAllowedInPageOfGroup[toGroupId]) {
                addPlaceholderItem(model->at(pageIndex)->count(), pageIndex, 1, 1, toGroupId);
                break;
            }
        }
    }

    if (d->placeholderAvailable()) {
        if (!d->itemIndexIsValid(d->placeholderItemInfo.itemIndex, d->placeholderItemInfo.pageIndex, model)) {
            qWarning() << "Move item to group failed, placeholder item index is not valid.";
            return;
        }
        moveItem->setPageIndex(d->placeholderItemInfo.pageIndex);
        model->at(d->placeholderItemInfo.pageIndex)->replace(
                    d->placeholderItemInfo.itemIndex, moveItem);
        moveItem->setState(d->globalState);
        moveItem->setNameVisible(true);
        d->layoutForReplacePlaceholderItem();
        d->layoutChangedPages.clear();
        d->clearPlaceholderItemInfo();
        updateCachedData(fromGroupId);
        updateCachedData(toGroupId);
        d->replaceGroupWithAppIcon(fromGroupId);
        d->groupAddPage(toGroupId);
        return;
    }

    for (int pageIndex = 0; pageIndex < model->count(); ++pageIndex) {
        if (model->at(pageIndex)->count() < d->maxItemCountAllowedInPageOfGroup[toGroupId]) {
            moveItem->setPageIndex(pageIndex);
            model->at(pageIndex)->push_back(moveItem);
            break;
        }
    }

    //分组最后一页是满的，会再添加一页
    updateCachedData(fromGroupId);
    updateCachedData(toGroupId);
    d->replaceGroupWithAppIcon(fromGroupId);
    d->groupAddPage(toGroupId);
}

void ModelManager::removeItem(int index,
                              int page,
                              int groupId)
{
    Q_D(ModelManager);
    MultiPageModel *model = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
    if (index == -1) {
        return;
    }
    bool editable = model->at(page)->at(index)->state() == LauncherItem::Editable ? true : false;
    model->at(page)->removeAt(index);

    if (groupId == d->kTaskBarGroupId || editable) {
        updateCachedData(groupId);
        return;
    }
    // 当前页为空时删除该页
    if (!d->needAddIcon) {
        if (model->at(page)->isEmpty()) {
            d->removeEmptyPage(model);
        }
    }
    updateCachedData(groupId);
}


void ModelManager::setItemState(int state, int itemId)
{
    Q_D(ModelManager);
    if (itemId < 0) {
        return;
    }

    auto *launcherItem = d->item(itemId);
    if (launcherItem) {
        launcherItem->setState(state);
        if (state == LauncherItem::Editable) {
            launcherItem->setNameVisible(true);
        }
    }
}

void ModelManager::setItemsInGroupState(int state, int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(d->groupModel.contains(groupId));
    auto groupModel = d->groupModel.value(groupId);
    for (const auto &pageItems : *groupModel) {
        for (const auto &item : *pageItems) {
            item->setState(state);
        }
    }

    if (d->needAddIcon) {
        if (state == LauncherItem::Editable) {
            d->addPageToContainer(groupId);
        } else if (state == LauncherItem::Normal) {
            d->removeEmptyPageFromContainer(groupId);
            if (d->item(groupId)->containerEditable()) {
                d->tryAddAddIcon(groupId);
            }
        }
    }
}

void ModelManager::setAllItemsState(int state)
{
    Q_D(ModelManager);
    if (d->globalState == state) {
        return;
    }
    d->globalState = state;
    itemStateChanged(state);
    d->setItemsStateInHomeScreen(state);
    d->setItemsStateInTaskbar(state);

    // 删除和添加空白页
    if (state == LauncherItem::Editable) {
        d->addBlankPageToAllModels();
    } else if (state == LauncherItem::Normal) {
        d->deleteBlankPageFromAllModels();
    }
}

void ModelManager::popupGroup(int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(d->groupModel.contains(groupId));

    LauncherItem *groupItem = d->item(groupId);
    if (groupItem) {
        groupItem->setState(LauncherItem::Popup);
    }

    d->setItemNameVisible(d->groupModel.value(groupId), true);
    setItemsInGroupState(d->globalState, groupId);
}

void ModelManager::minimizeGroup(int groupId)
{
    Q_D(ModelManager);

    if (!d->groupModel.contains(groupId)) {
        return;
    }

    setItemsInGroupState(LauncherItem::Normal, groupId);
    d->setItemNameVisible(d->groupModel.value(groupId), false);

    // 恢复分组内的item的状态
    LauncherItem *groupItem = d->item(groupId);
    if (groupItem) {
        groupItem->setState(d->globalState);
    }

    // 新建分组之后又取消了，比如拖动图标至另一个图标之上，弹出新建分组窗口
    // 之后未释放图标，一直拖拽出分组边界
    if (d->placeholderItemInfo.parentItemType == ModelManagerPrivate::Group &&
            d->placeholderItemInfo.groupId == groupId) {
        removePlaceholderItem();
    }

    d->replaceGroupWithAppIcon(groupId);

    if (d->groupIsEmpty(groupId)) {
        d->removeGroupFromDesktop(groupId);
    }
}

int ModelManager::createPopupGroupModel(int underItemId,
                                        int aboveItemId,
                                        bool popup)
{
    Q_D(ModelManager);

    int newId = d->availableId();

    LauncherItem *underItem = d->item(underItemId);
    LauncherItem *aboveItem = d->item(aboveItemId);
    if (!underItem || !aboveItem) {
        qWarning() << "create popup group failed, under item id: " << underItemId
                   << "above item id: " << aboveItemId;
        return - 1;
    }

    QString groupName, friendlyName;
    std::tie(groupName, friendlyName) = d->createNewGroupName(underItem, aboveItem);
    auto *groupModel = d->createGroup(newId, friendlyName, underItem);

    int removeItemGroupId = -1;
    // 弹出分组窗口，新建分组
    if (popup) {
        addPlaceholderItem(1, 0, 1, 1, newId);
        underItem->setState(LauncherItem::Popup);
    } // 直接新建分组，适用于拖动图标至另一个图标之上
    else {
        removeItemGroupId = d->removeAboveItem(newId, groupModel->first(), aboveItem);
    }

    Q_EMIT createGroup(newId, groupName, QList<int>() << underItemId << aboveItemId);
    updateCachedData(newId);
    d->replaceGroupWithAppIcon(removeItemGroupId);
    removePlaceholderItem();
    return newId;
}

void ModelManager::setGroupName(const QString &name, int groupId)
{
    Q_D(ModelManager);
    if (!d->groupModel.contains(groupId)) {
        return;
    }
    d->iterateMultiPageModelWithItemCallBack(&d->launcherModel, [this, name, groupId](LauncherItem *item) {
        if ((item->type() == LauncherItem::Container
             || item->type() == LauncherItem::Group) &&
                item->id() == groupId) {
            if (item->name() == name) {
                return;
            }
            item->setName(name);
            Q_EMIT groupNameChanged(name, groupId);
        }
    });
}

void ModelManager::setAddAppCategory(int category, int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(d->groupModel.contains(groupId));
    d->currentCategoryOfAddion = category;
    d->initSelectedItemIds(groupId);

    int startPage = 0, endPage = 0;
    if (category == CategoryItem::All) {
        startPage = 0;
        endPage = d->categoryItemsModel.count() - 1;//d->categoryModel.first()->pageCount() - 1;
    } else {
        for (int index=0; index<d->categoryModel.count(); ++index) {
            if (d->categoryModel.at(index)->category() == category) {
                -- startPage;
                endPage = startPage + d->categoryModel.at(index)->pageCount();
                break;
            }
            startPage += d->categoryModel.at(index)->pageCount();
        }
    }
    for (int page=0; page<d->categoryItemsModel.count(); ++page) {
        int pageItemCount = d->categoryItemsModel.at(page)->count();
        for (int itemIndex=0; itemIndex<pageItemCount; ++itemIndex) {
            auto item = d->categoryItemsModel.at(page)->at(itemIndex);
            if (((category == CategoryItem::All
                  && !d->categoryContainsItem(item->id(), CategoryItem::Education)
                  && d->categoryOfAddtionPage(page) != CategoryItem::Education)
                 || (category != CategoryItem::All && d->dataHandler->categoriesItems()
                     .value(UKUI::BaseItem::Categories::Education).contains(item->id()))) &&
                    page >= startPage && page <= endPage) {
                item->setState(LauncherItem::Selectable);
                int itemGroupId = d->itemGroupId(item->id());
                if (itemGroupId == -1) {
                    item->setSelected(false);
                } else {
                    if (itemGroupId == groupId) {
                        item->setSelected(true);
                    } else {
                        item->setState(LauncherItem::Normal);
                    }
                }
            } else {
                item->setState(LauncherItem::Normal);
            }
        }
    }
}

void ModelManager::updateCachedData(int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(groupId >= -1);
    if (groupId >= 0 && !d->groupModel.contains(groupId)) {
        return;
    }
    qDebug() << __LINE__ << __func__ << groupId;
    MultiPageModel *model = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
    QList<QList<int>> groupItemIds;
    for (int page=0; page<model->count(); ++page) {
        QList<int> itemIds;
        for (int itemIndex=0;
             itemIndex < model->at(page)->count();
             ++ itemIndex) {
            int id = model->at(page)->at(itemIndex)->id();
            if (id == 0) {
                qDebug() << __func__ << id;
                continue;
            }
            if (model->at(page)->at(itemIndex)->type() != LauncherItem::WindowIcon) {
                itemIds.push_back(id);
            }
        }
        if (!itemIds.isEmpty() || groupId == d->kTaskBarGroupId) {
            groupItemIds.push_back(itemIds);
        }
    }
    if (groupId == -1) {
        Q_EMIT updateHomeScreenPage(groupItemIds);
    } else {
        Q_EMIT updateGroup(groupId, groupItemIds);
    }
}

bool ModelManager::groupEditable(int groupId)
{
    Q_D(ModelManager);
    if (d->item(groupId) == nullptr) {
        return false;
    }
    return d->item(groupId)->containerEditable();
}

//edu-screen
void ModelManager::updateGroupItems(int groupId, int pageIndex)
{
    Q_D(ModelManager);
    Q_ASSERT(d->groupModel.contains(groupId));
    auto selectedIds = d->selectedIds();

    // add
    for (const auto &id : qAsConst(selectedIds)) {
        if (!d->itemExistsInGroup(groupId, id)) {
            d->autoPushBackItemForGroup(id, groupId, pageIndex);
        }
    }
    // remove
    auto groupIds = d->itemIdsInGroup(groupId);
    for (const auto &id : groupIds) {
        // 添加按钮的id是0
        if (!selectedIds.contains(id) && id != 0) {
            d->takeAppIdFromGroup(id, groupId);
        }
    }
    updateCachedData(groupId);
}

void ModelManager::setItemSelectState(int itemId,
                                      int pageIndex,
                                      bool selected)
{
    Q_D(ModelManager);
    if (d->categoryItemsModel.isEmpty()) {
        return;
    }
    if (d->categoryItemsModel.count() <= pageIndex || pageIndex < 0) {
        return;
    }

    if (selected) {
        d->currentSelectedIds.push_front(itemId);
    } else {
        d->currentSelectedIds.removeOne(itemId);
    }

    for (int page=0; page<d->categoryItemsModel.count(); ++page) {
        int itemCount = d->categoryItemsModel.at(page)->count();
        for (int itemIndex=0; itemIndex<itemCount; ++itemIndex) {
            auto item = d->categoryItemsModel.at(page)->at(itemIndex);
            if (item->id() == itemId && item->state() == LauncherItem::Selectable) {
                item->setSelected(selected);
            }
        }
    }
}

void ModelManager::setGroupState(int state, int groupId)
{
    Q_ASSERT(groupId > 0);
    Q_D(ModelManager);
    if (!d->groupModel.contains(groupId)) {
        qWarning() << "The group " << groupId << " dose not exist!";
        return;
    }
#ifdef QT_DEBUG
    qDebug() << __FUNCTION__ << state << groupId;
#endif

    auto *groupItem = d->item(groupId);
    groupItem->setState(state);
    d->updateItemStatusInGroupAfterGroupStateChanged(groupId, state);
}

void ModelManager::addPlaceholderItem(int index,
                                      int pageIndex,
                                      int rowSpan,
                                      int columnSpan,
                                      int groupId)
{
    Q_D(ModelManager);

    qDebug() << "Add placeholder item " << index << pageIndex << groupId;
    if (index < 0 || pageIndex < 0) {
        return;
    }
    MultiPageModel *model = d->availableModel(groupId);

    if (model == nullptr) {
        return;
    }
    // main page
    if (groupId == -1 && !d->placeholderAvailable()) {
        //将应用移动到pageIndex页之后，pageIndex页所占网格总数
        int pageGridCount = d->gridCount(groupId, pageIndex) + rowSpan*columnSpan;
        //pageIndex页最后一个item索引
        int lastItemIndex = model->at(pageIndex)->count() - 1;
        while (pageGridCount > d->pageItemMaxCount && lastItemIndex >= 0) {
            --index;
            //item所占网格总数
            int itemGridCount = model->at(pageIndex)->at(lastItemIndex)->rowSpan() *
                    model->at(pageIndex)->at(lastItemIndex)->columnSpan();
            pageGridCount -= itemGridCount;
            --lastItemIndex;
        }
        if (index < 0) {
            qWarning() << __func__ << "Add placeholder failed, the item index is not avalid.";
            return;
        }
    }
    if (groupId > 0 && !d->placeholderAvailable()) {
        if (index >= d->kMaxItemCountInGroupPage) {
            --index;
        }
    }


    if (groupId > 0 && pageIndex > model->count() - 1) {
        return;
    }

    if (d->placeholderAvailable()) {
        // 如果是在同一页的占位 item,则直接移动占位 item 即可
        if (d->placeholderItemInfo.pageIndex == pageIndex &&
                d->placeholderItemInfo.groupId == groupId) {
            MultiPageModel *moveModel = groupId >= 0 ? d->groupModel.value(groupId) : &d->launcherModel;
            int moveModelPageItemCount = moveModel->at(pageIndex)->count();
            if (d->placeholderItemInfo.itemIndex >= moveModelPageItemCount) {
                d->placeholderItemInfo.itemIndex = moveModelPageItemCount - 1;
            }
            if (index >= moveModelPageItemCount) {
                index = moveModelPageItemCount - 1;
            }
            moveItem(d->placeholderItemInfo.itemIndex, index, pageIndex, groupId);
            d->placeholderItemInfo.itemIndex = index;
            return;
        }
        // in the main page
        if (d->placeholderItemInfo.groupId == -1) {
            d->launcherModel.at(d->placeholderItemInfo.pageIndex)->removeAt(d->placeholderItemInfo.itemIndex);
        } else {
            model->at(d->placeholderItemInfo.pageIndex)->removeAt(d->placeholderItemInfo.itemIndex);
        }
    }
    LauncherItem *placeholderItem = new LauncherItem;
    d->placeholderItemInfo.placeholderItem = placeholderItem;
    d->placeholderItemInfo.parentItemType = groupId >= 0 ?
                ModelManagerPrivate::Group : ModelManagerPrivate::Page;
    d->placeholderItemInfo.itemIndex = index;
    d->placeholderItemInfo.pageIndex = pageIndex;
    d->placeholderItemInfo.groupId = groupId;

    placeholderItem->setRowSpan(rowSpan);
    placeholderItem->setColumnSpan(columnSpan);
    placeholderItem->setPageIndex(pageIndex);
    placeholderItem->setType(LauncherItem::Placeholder);

#ifdef EDU_SCREEN
    // 不可编辑的分组不存在添加按钮，所有需要push_back
    if (d->item(groupId)->containerEditable()) {
        model->at(pageIndex)->insert(index, placeholderItem);
    } else {
        model->at(pageIndex)->push_back(placeholderItem);
        d->placeholderItemInfo.itemIndex = model->at(pageIndex)->count() - 1;
        qDebug() << "d->placeholderItemInfo.itemIndex2: " << d->placeholderItemInfo.itemIndex;
    }
#else
    model->at(pageIndex)->insert(index, placeholderItem);
#endif
    d->layoutForAddPlaceholderItem(pageIndex, groupId);
}

void ModelManager::removePlaceholderItem()
{
    Q_D(ModelManager);
    qDebug() << "Remove placeholder item";
    if (!d->placeholderAvailable()) {
        return;
    }
    if (d->placeholderItemInfo.parentItemType == ModelManagerPrivate::Page) {
        if (!d->itemIndexIsValid(d->placeholderItemInfo.itemIndex,
                                 d->placeholderItemInfo.pageIndex,
                                 &d->launcherModel)) {
            return;
        }
        d->launcherModel[d->placeholderItemInfo.pageIndex]->removeAt(d->placeholderItemInfo.itemIndex);
    } else if (d->placeholderItemInfo.parentItemType == ModelManagerPrivate::Group) {
        if (!d->groupModel.contains(d->placeholderItemInfo.groupId) ||
                !d->itemIndexIsValid(d->placeholderItemInfo.itemIndex,
                                     d->placeholderItemInfo.pageIndex,
                                     d->groupModel.value(d->placeholderItemInfo.groupId))) {
            return;
        }
        d->groupModel[d->placeholderItemInfo.groupId]->
                at(d->placeholderItemInfo.pageIndex)->removeAt(d->placeholderItemInfo.itemIndex);
    }
    d->layoutForRemovePlaceholderItem();
    d->clearPlaceholderItemInfo();
}

void ModelManager::replacePlaceholderItem(int fromIndex, int fromPage, int groupId)
{
    Q_D(ModelManager);
    Q_ASSERT(fromIndex >=0 && fromPage >=0 && groupId >= -1);
    if (!d->placeholderAvailable()) {
        return;
    }
#ifdef QT_DEBUG
    qDebug() << __FUNCTION__ << "group id, from page, index" << groupId << fromPage << fromIndex;
#endif

    LauncherItem *moveItem = nullptr;
    MultiPageModel *fromModel = d->availableModel(groupId);
    MultiPageModel *toModel = d->availableModel(d->placeholderItemInfo.groupId);
    if (fromModel == nullptr || toModel == nullptr) {
        return;
    }
    if (!d->itemIndexIsValid(fromIndex, fromPage, fromModel) ||
            !d->itemIndexIsValid(d->placeholderItemInfo.itemIndex, d->placeholderItemInfo.pageIndex, toModel)) {
        qWarning() << "Replace placeholder failed, the item index is not avalid.";
        return;
    }

    if (fromModel->at(fromPage)->at(fromIndex)->type() != LauncherItem::AppIcon &&
            fromModel->at(fromPage)->at(fromIndex)->type() != LauncherItem::Group &&
            fromModel->at(fromPage)->at(fromIndex)->type() != LauncherItem::Widget) {
        return;
    }

    moveItem = fromModel->at(fromPage)->takeAt(fromIndex);
    moveItem->setPageIndex(d->placeholderItemInfo.pageIndex);
    toModel->at(d->placeholderItemInfo.pageIndex)->replace(
                d->placeholderItemInfo.itemIndex, moveItem);
    moveItem->setState(LauncherItem::Editable);
    moveItem->setNameVisible(true);

#ifndef EDU_SCREEN
    // 如果分组内只有一个应用，删除该分组
    if (!d->needAddIcon) {
        if (groupId > 0) {
            d->replaceGroupWithAppIcon(groupId);
            /// \todo 根据操作的类型进行选择性的更新
            updateCachedData(groupId);
        }
        updateCachedData();
    }
#endif
#ifdef EDU_SCREEN
    d->tryAddAddIcon(fromPage, groupId);
    d->layoutForReplacePlaceholderItem();
#endif
    d->layoutChangedPages.clear();
    d->clearPlaceholderItemInfo();
}
bool ModelManager::replacePlaceholderItemTaskbar(int fromIndex,
                                                 int fromPage,
                                                 int fromgroupId,
                                                 int togroupId)
{
    Q_D(ModelManager);
    qDebug() << __FUNCTION__ << fromIndex << fromPage << fromgroupId << togroupId;
    if (!d->placeholderAvailable()) {
        return false;
    }
    if (d->placeholderItemInfo.groupId == fromgroupId) {
        return false;
    }
    LauncherItem *moveItem = nullptr;
    MultiPageModel *model = fromgroupId >= 0 ?
                d->groupModel.value(fromgroupId) : &d->launcherModel;

    if (model->at(fromPage)->at(fromIndex)->type() != LauncherItem::AppIcon) {
        return false;
    }

    moveItem = model->at(fromPage)->takeAt(fromIndex);
    moveItem->setPageIndex(d->placeholderItemInfo.pageIndex);

    int windowOpenItemIndex = -1;
    if (togroupId == 0) {
        for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
            if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile() == moveItem->desktopFile()) {
                windowOpenItemIndex = itemIndex;
                moveItem->setAppOpen(true);
                moveItem->setWindowId(d->groupModel[0]->at(0)->at(itemIndex)->windowId());
                break;
            }
        }
    }
    if (togroupId == -1) {
        moveItem->setNameVisible(true);
    }
    MultiPageModel *desktopModel = togroupId >= 0 ?
                d->groupModel.value(togroupId) : &d->launcherModel;

    desktopModel->at(d->placeholderItemInfo.pageIndex)->replace(
                d->placeholderItemInfo.itemIndex, moveItem);

    if (windowOpenItemIndex > -1) {
        removeItem(windowOpenItemIndex, 0, 0);
    }
    if (fromgroupId > 0) {
        d->replaceGroupWithAppIcon(fromgroupId);
        /// \todo 根据操作的类型进行选择性的更新
        updateCachedData();
        updateCachedData(fromgroupId);
    }
    d->layoutChangedPages.clear();
    d->clearPlaceholderItemInfo();
    return true;
}

QAbstractListModel* ModelManager::taskbarModel() //const
{
    Q_D(ModelManager);
    if(!d->groupModel.contains(0)) {
        MultiPageModel *multiPage = new MultiPageModel;
        multiPage->push_back(new PageModel);
        d->groupModel[0] = multiPage;
    }
    return d->groupModel[0]->at(0);
}

void ModelManager::setAppOpenState(QString desktopName, WId wid)
{
    Q_D(const ModelManager);
    for(int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        if(d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().endsWith(desktopName)) {
            d->groupModel[0]->at(0)->at(itemIndex)->setAppOpen(true);
            d->groupModel[0]->at(0)->at(itemIndex)->setWindowId(wid);
            return;
        }
    }
    qDebug() << "desktopName cannot be found";
}

void ModelManager::setAppCloseState(QString desktopName)
{
    Q_D(const ModelManager);
    for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().endsWith(desktopName)) {
            d->groupModel[0]->at(0)->at(itemIndex)->setAppOpen(false);
            return;
        }
    }
    qDebug() << "desktopName cannot be found";
}

void ModelManager::addWindowIconToTaskbar(const QString &iconPath,
                                          const QString &desktopName,
                                          const QString &appName,
                                          const int &wid)
{
    Q_D(ModelManager);
    LauncherItem* item = new LauncherItem();
    item->setId(d->availableId());
    item->setPageIndex(0);
    item->setColumnSpan(1);
    item->setRowSpan(1);
    item->setType(LauncherItem::WindowIcon);
    item->setIcon(iconPath);
    item->setDesktopFile(desktopName);
    item->setAppOpen(true);
    item->setWindowId(wid);
    item->setName(appName);
    if (d->isAppInTheWhiteList(desktopName)) {
        item->setUninstallable(false);
    }
    if (d->groupModel.contains(d->kTaskBarGroupId) && d->groupModel.value(d->kTaskBarGroupId) != nullptr && d->groupModel[0]->count() != 0) {
        d->groupModel[0]->at(0)->append(item);
    }
}

void ModelManager::removeAppIconFromTaskBar(const QString &desktopFileName)
{
    Q_D(const ModelManager);
    for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        if (d->groupModel.value(0)->at(0)->at(itemIndex)->desktopFile().contains(desktopFileName) &&
                d->groupModel.value(0)->at(0)->at(itemIndex)->type() == LauncherItem::WindowIcon) {
            d->groupModel.value(0)->at(0)->removeAt(itemIndex);
            return;
        }
    }
}

QString ModelManager::findIconPath(const QString &desktopName)
{
    Q_D(const ModelManager);
    for (int pageIndex=0; pageIndex < d->launcherModel.count(); ++pageIndex) {
        for (int itemIndex=0; itemIndex < d->launcherModel.at(pageIndex)->count(); ++itemIndex) {
            if (d->launcherModel.at(pageIndex)->at(itemIndex)->type() == LauncherItem::Group) {
                int groupId = d->launcherModel.at(pageIndex)->at(itemIndex)->id();
                auto groupModel = d->groupModel.value(groupId);
                for (const auto &pageItems : *groupModel) {
                    for (const auto &item : *pageItems) {
                        if (item->desktopFile().contains(desktopName)) {
                            qDebug() << "group iconPath---->" << item->icon();
                            return item->icon();
                        }
                    }
                }
            }

            if ((d->launcherModel.at(pageIndex)->at(itemIndex)->desktopFile() == desktopName)) {
                 return d->launcherModel.at(pageIndex)->at(itemIndex)->icon();
            }
        }
    }
    return QString();
}

QString ModelManager::findDesktopName(const QString &desktopName)
{
    Q_D(const ModelManager);
    for (int pageIndex=0; pageIndex < d->launcherModel.count(); ++pageIndex) {
        for (int itemIndex=0; itemIndex < d->launcherModel.at(pageIndex)->count(); ++itemIndex) {
            if (d->launcherModel.at(pageIndex)->at(itemIndex)->type() == LauncherItem::Group) {
                int groupId = d->launcherModel.at(pageIndex)->at(itemIndex)->id();
                auto groupModel = d->groupModel.value(groupId);
                for (const auto &pageItems : *groupModel) {
                    for (const auto &item : *pageItems) {
                        if (item->desktopFile() == desktopName) {
                            return item->desktopFile();
                        }
                        QString groupDesktopName = item->desktopFile();
                        if (groupDesktopName.split("/").empty()) {
                            qWarning() << __func__ << groupDesktopName << desktopName;
                            return QString();
                        }
                        groupDesktopName = groupDesktopName.split("/").last();
                        if (groupDesktopName == desktopName) {
                            return item->desktopFile();
                        }
                    }
                }
            }
            QString tmpDesktopName = d->launcherModel.at(pageIndex)->at(itemIndex)->desktopFile();
            if (tmpDesktopName == desktopName) {
                return tmpDesktopName;
            }
            if (tmpDesktopName.split("/").empty()) {
                qWarning() << __func__ << tmpDesktopName << desktopName;
                return QString();
            }
            tmpDesktopName = tmpDesktopName.split("/").last();
            if (tmpDesktopName == desktopName) {
                return d->launcherModel.at(pageIndex)->at(itemIndex)->desktopFile();
            }
        }
    }

    return QString();
}

QString ModelManager::findAppName(const QString &desktopName)
{
    Q_D(const ModelManager);
    for (int pageIndex=0; pageIndex < d->launcherModel.count(); ++pageIndex) {
        for (int itemIndex=0; itemIndex < d->launcherModel.at(pageIndex)->count(); ++itemIndex) {
            if (d->launcherModel.at(pageIndex)->at(itemIndex)->type() == LauncherItem::Group) {
                int groupId = d->launcherModel.at(pageIndex)->at(itemIndex)->id();
                auto groupModel = d->groupModel.value(groupId);
                for (const auto &pageItems : *groupModel) {
                    for (const auto &item : *pageItems) {
                        if (item->desktopFile().contains(desktopName)) {
                            return item->name();
                        }
                    }
                }
            }

            if ((d->launcherModel.at(pageIndex)->at(itemIndex)->desktopFile().endsWith(desktopName))) {
                return d->launcherModel.at(pageIndex)->at(itemIndex)->name();
            }
        }
    }
    return QString();
}


QString ModelManager::findDesktopNameFromTaskbar(const QString &desktopName)
{
    Q_D(ModelManager);
    if (!d->groupModel.contains(d->kTaskBarGroupId) ||
            !d->itemIndexIsValid(0, 0, d->groupModel.value(d->kTaskBarGroupId))) {
        return QString();
    }
    for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
        QString tmpDesktopName = d->groupModel[0]->at(0)->at(itemIndex)->desktopFile();
        if (tmpDesktopName == desktopName) {
            return tmpDesktopName;
        }
        if (tmpDesktopName.split("/").empty()) {
            qWarning() << __func__ << tmpDesktopName << desktopName;
            return QString();
        }
        tmpDesktopName = tmpDesktopName.split("/").last();
        if (tmpDesktopName == desktopName) {
            return d->groupModel[0]->at(0)->at(itemIndex)->desktopFile();
        }
    }
    return QString();
}

void ModelManager::activeWindow(const quint32 &wid)
{
    Q_D(const ModelManager);
//    d->taskBarManager->appWindowOpen(wid);
}

void ModelManager::changeAppWindowWid(const QString &desktopName,
                                      const int &wid,
                                      const int &newWid)
{
    Q_D(const ModelManager);
    if (!findDesktopName(desktopName).isEmpty()) {
        for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
            if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().contains(desktopName)) {
               if (d->groupModel[0]->at(0)->at(itemIndex)->windowId() == wid) {
                   d->groupModel[0]->at(0)->at(itemIndex)->setWindowId(newWid);
               }
            }
        }
    }
    else {
        for (int itemIndex = 0; itemIndex < d->groupModel[0]->at(0)->count(); itemIndex++) {
            if (d->groupModel[0]->at(0)->at(itemIndex)->desktopFile().contains(desktopName)) {
                if (d->groupModel[0]->at(0)->at(itemIndex)->windowId() == wid) {
                    d->groupModel[0]->at(0)->at(itemIndex)->setWindowId(newWid);
                }
            }
        }
    }
}

bool ModelManager::itemExistsOnDesktop(const int &itemId, const int &pageIndex)
{
    Q_D(ModelManager);
    if (pageIndex >= d->launcherModel.count() || pageIndex < 0) {
        return false;
    }
    for (int itemIndex = 0; itemIndex < d->launcherModel.at(pageIndex)->count(); itemIndex++) {
        if (d->launcherModel.at(pageIndex)->at(itemIndex)->id() == itemId) {
            return true;
        }
    }
    return false;
}

int ModelManager::containerCategory(const int containerId)
{
    Q_D(ModelManager);
    auto containerItem = d->item(containerId);
    if (containerItem != nullptr) {
        return containerItem->containerCategory();
    }
    return -1;
}

void ModelManager::initDesktopData(const QMap<int, QList<int> > &pageData,
                                   const QMap<int, QList<QList<int>>> &setData,
                                   const QMap<int, UKUI::BaseItem *> &itemData)
{
    Q_D(ModelManager);
    d->getAppWhiteList();

    // taskbar
    if (setData.contains(d->kTaskBarGroupId) &&
            !setData.value(d->kTaskBarGroupId).isEmpty()) {
        d->initTaskBarData(setData.value(d->kTaskBarGroupId).at(0), itemData);
    }

    QMap<int, QList<int>>::const_iterator i = pageData.constBegin();
    while (i != pageData.constEnd()) {
        PageModel *pageModel = new PageModel;
        PageModel groupPageModel, containerPageModel;
        for (const auto &itemId : i.value()) {
            UKUI::BaseItem *item = itemData.value(itemId);
            LauncherItem *launcherItem = new LauncherItem;
            if (!launcherItem || !item) {
                continue;
            }
            switch (item->getType()) {
            case UKUI::Type::Icon: {
                launcherItem = d->createLauncherItem(LauncherItem::AppIcon, item, i.key());
                if (d->isAppInTheWhiteList(launcherItem->desktopFile())) {
                    launcherItem->setUninstallable(false);
                }
                break;
            }
            case UKUI::Type::Group: {
                launcherItem = d->createLauncherItem(LauncherItem::Group, item, i.key());
                launcherItem->setUninstallable(false);

                MultiPageModel *groupModel = new MultiPageModel;
                QList<QList<int>> childItemsId = setData.value(item->getId());
                int page = 0;
                for (auto const &pageItemIds : childItemsId) {
                    PageModel *groupPageModel = new PageModel;
                    for (auto const &itemId : pageItemIds) {
                        LauncherItem *item = d->createLauncherItem(LauncherItem::AppIcon, itemData.value(itemId), page);
                        if (item) {
                            item->setNameVisible(false);
                            groupPageModel->push_back(item);
                            if (d->isAppInTheWhiteList(item->desktopFile())) {
                                item->setUninstallable(false);
                            }
                        }
                    }
                    ++ page;
                    groupModel->push_back(groupPageModel);
                }
                d->groupModel.insert(item->getId(), groupModel);
                launcherItem->setModel(groupModel);
                d->maxItemCountAllowedInPageOfGroup[item->getId()] = d->kMaxItemCountInGroupPage;
                break;
            }

            case UKUI::Type::Container: {
                UKUI::ContainerItem *containerItem = static_cast<UKUI::ContainerItem *>(item);
                if (!containerItem) {
                    break;
                }
                launcherItem->setId(containerItem->getId());
                launcherItem->setName(containerItem->getName());
                launcherItem->setType(LauncherItem::Container);
                launcherItem->setPageIndex(i.key());
                launcherItem->setRowSpan(containerItem->getPlaceHolder().at(0));
                launcherItem->setColumnSpan(containerItem->getPlaceHolder().at(1));
                launcherItem->setUninstallable(false);
                launcherItem->setTitleEditable(containerItem->isTitleEditable());
                launcherItem->setContainerEditable(containerItem->isContainerEditable());
                QMetaEnum meatCategory = QMetaEnum::fromType<CategoryItem::Category>();
                int category = meatCategory.keyToValue(containerItem->getContainerCategory().toStdString().data());
                launcherItem->setContainerCategory(category);

                int maxItemCountInPageOfContainer = (launcherItem->rowSpan() - 1) * launcherItem->columnSpan();
                d->maxItemCountAllowedInPageOfGroup[launcherItem->id()] = maxItemCountInPageOfContainer;

                MultiPageModel *groupModel = new MultiPageModel;
                QList<QList<int>> childItemsId = setData.value(containerItem->getId());
                for (int page=0; page<childItemsId.count(); ++page) {
                    PageModel *groupPageModel = new PageModel;
                    int itemCount = childItemsId.at(page).count();
                    for (int itemIndex=0; itemIndex<itemCount; ++itemIndex) {
                        LauncherItem *groupItem = new LauncherItem;
                        UKUI::IconItem *appItem =
                                static_cast<UKUI::IconItem *>(itemData[childItemsId.at(page).at(itemIndex)]);
                        if (appItem) {
                            groupItem->setId(appItem->getId());
                            groupItem->setName(appItem->getName());
                            groupItem->setType(LauncherItem::AppIcon);
                            groupItem->setPageIndex(page);
                            groupItem->setIcon(appItem->getIconName());
                            groupItem->setDesktopFile(appItem->getDesktopName());
                            groupItem->setRowSpan(1);
                            groupItem->setColumnSpan(1);
                            groupPageModel->push_back(groupItem);
                            if (d->isAppInTheWhiteList(groupItem->desktopFile())) {
                                groupItem->setUninstallable(false);
                            }
                        }
                    }
                    if (d->needAddIcon &&
                            groupPageModel->count() < d->maxItemCountAllowedInPageOfGroup[containerItem->getId()] &&
                            launcherItem->containerEditable()) {
                        LauncherItem *addItem = d->createAddIconItem(page);
                        groupPageModel->push_back(addItem);
                    }
                    groupModel->push_back(groupPageModel);
                }
                if (launcherItem->containerEditable() &&
                        groupModel->at(groupModel->count()-1)->count() == maxItemCountInPageOfContainer &&
                        groupModel->at(groupModel->count()-1)->last()->type() == LauncherItem::AppIcon) {
                    groupModel->push_back(new PageModel);
                    groupModel->last()->push_back(d->createAddIconItem(groupModel->count()-1));
                }
                d->groupModel.insert(containerItem->getId(), groupModel);
                break;
            }

            case UKUI::Type::Widget: {
                launcherItem = d->createLauncherItem(LauncherItem::Widget, item, i.key());
                break;
            }

            case UKUI::Type::Unknown:
                break;
            default:
                break;
            }
            pageModel->push_back(launcherItem);
        }
        d->launcherModel.push_back(pageModel);
        ++i;
    }
    // init task bar
    QList<WId> runningAppWindows = KWindowSystem::windows();
    for (auto &wid : runningAppWindows) {
        KWindowInfo winInfo(wid, 0, NET::WM2DesktopFileName);
        QString desktopName = d->appManager->AppDesktopFileNameByWid(wid);
        if(desktopName.isEmpty()) {
            continue;
        }
        QString desktopname = findDesktopNameFromTaskbar(desktopName);
        QString appName = findAppName(desktopName);
        if (desktopname.isEmpty()) {
            desktopname = findDesktopName(desktopName);
            if(!desktopname.isEmpty()) {
                QString filePathName = findIconPath(desktopname);
                d->taskBarManager->setWindowDesktopFileName(wid, desktopname);
                addWindowIconToTaskbar(filePathName, desktopname, appName, wid);
            }
        } else {
            //该应用在任务栏，设置为打开状态
            setAppOpenState(desktopname, wid);
            d->taskBarManager->setWindowDesktopFileName(wid, desktopname);
        }
    }
    if (!d->needAddIcon) {
        Q_EMIT desktopDataInited();
    }
}

void ModelManager::categories(const QMap<UKUI::BaseItem::Categories, QList<UKUI::ItemId>> categories)
{
    Q_D(ModelManager);
    d->categoryModel.clear();
    d->categoryItemsModel.clear();

    CategoryItem *allCategoryItem = new CategoryItem;
    int pageCount = d->dataHandler->apps().count() % d->pageItemMaxOfAddition ?
                d->dataHandler->apps().count() / d->pageItemMaxOfAddition + 1 :
                d->dataHandler->apps().count() / d->pageItemMaxOfAddition;
    allCategoryItem->setCategory(CategoryItem::All);
    QString allCategoryName = d_ptr->dataHandler->translatedName(
                AppCategoryManager::categoryToString(UKUI::BaseItem::All));
    allCategoryItem->setName(allCategoryName);
    allCategoryItem->setNormalIcon(d->categoryIcon(CategoryItem::All).first);
    allCategoryItem->setClickIcon(d->categoryIcon(CategoryItem::All).second);
    allCategoryItem->setPageCount(pageCount);
    d->categoryModel.push_back(allCategoryItem);

    // All
    auto apps = d->dataHandler->apps();
    auto appIt = apps.constBegin();
    PageModel *pageModel = new PageModel;
    while (appIt != apps.constEnd()) {
        UKUI::IconItem *iconItem =
                static_cast<UKUI::IconItem *>(d->dataHandler->items().value(*appIt));
        LauncherItem *launcherItem = new LauncherItem;
        launcherItem->setId(*appIt);
        launcherItem->setName(iconItem->getName());
        launcherItem->setIcon(iconItem->getIconName());
        launcherItem->setPageIndex(d->categoryItemsModel.isEmpty() ? 0 : d->categoryItemsModel.count());
        pageModel->push_back(launcherItem);
        if (pageModel->count() == d->pageItemMaxOfAddition) {
            d->categoryItemsModel.push_back(pageModel);
            pageModel = new PageModel;
        }
        ++ appIt;
    }
    if (!pageModel->isEmpty()) {
        d->categoryItemsModel.push_back(pageModel);
    } else {
        pageModel->deleteLater();
    }
    //

    auto it = categories.constBegin();
    while (it != categories.constEnd()) {
        if (it.value().isEmpty()) {
            ++ it;
            continue;
        }

        int category = static_cast<int>(it.key());
        CategoryItem *categoryItem = new CategoryItem;
        int pageCount = it.value().count() % d->pageItemMaxOfAddition ?
                    it.value().count() / d->pageItemMaxOfAddition + 1 :
                    it.value().count() / d->pageItemMaxOfAddition;
        categoryItem->setCategory(category);
        QString categoryName = d_ptr->dataHandler->translatedName(AppCategoryManager::categoryToString(it.key()));
        categoryItem->setName(categoryName);
        categoryItem->setPageCount(pageCount);
        categoryItem->setNormalIcon(d->categoryIcon(categoryItem->category()).first);
        categoryItem->setClickIcon(d->categoryIcon(categoryItem->category()).second);

        auto itemIt = it.value().constBegin();
        PageModel *pageModel = new PageModel;
        while (itemIt != it.value().constEnd()) {
            UKUI::IconItem *iconItem =
                    static_cast<UKUI::IconItem *>(d->dataHandler->items().value(*itemIt));
            LauncherItem *launcherItem = new LauncherItem;
            launcherItem->setName(iconItem->getName());
            launcherItem->setIcon(iconItem->getIconName());
            launcherItem->setId(*itemIt);
            launcherItem->setPageIndex(d->categoryItemsModel.isEmpty() ? 0 : d->categoryItemsModel.count());
            pageModel->push_back(launcherItem);
            if (pageModel->count() == d->pageItemMaxOfAddition) {
                d->categoryItemsModel.push_back(pageModel);
                pageModel = new PageModel;
            }
            ++ itemIt;
        }
        if (!pageModel->isEmpty()) {
            d->categoryItemsModel.push_back(pageModel);
        } else {
            pageModel->deleteLater();
        }

        d->categoryModel.push_back(categoryItem);
        ++ it;
    }
}

void ModelManager::removeItemFromGroup(int itemId, int groupId)
{
    Q_D(ModelManager);
    QPair<int, int> itemPos = d->itemPosInGroup(itemId, groupId);
    int itemPageIndex = itemPos.first;
    int itemIndex = itemPos.second;
    if (itemPageIndex == -1) {
        qWarning() << __func__ << "itemId or groupId not exists !!!";
        return;
    }
    auto model = d->groupModel.value(groupId);
    auto itemState = model->at(itemPageIndex)->at(itemIndex)->state();
    QString itemDesktopName = model->at(itemPageIndex)->at(itemIndex)->desktopFile();
    model->at(itemPageIndex)->removeAt(itemIndex);
    if (!d->needAddIcon) {
        removeAppIconFromTaskBar(itemDesktopName);
        if (itemState == LauncherItem::Normal && groupId != d->kTaskBarGroupId &&
                model->at(itemPageIndex)->count() == 0) {
            d->removeEmptyPage(model);
        }
        if (d->getGroupState(groupId) != LauncherItem::Popup) {
            d->replaceGroupWithAppIcon(groupId);
        }
    } else {
        d->removeItemFromCategory(itemId);
        if (itemState == LauncherItem::Normal) {
            d->removeEmptyPageFromContainer(groupId);
            if (d->item(groupId)->containerEditable()) {
                d->tryAddAddIcon(groupId);
            }
        }
    }
}

void ModelManager::removeItemWithoutGroup(int itemId, int pageIndex)
{
    Q_D(ModelManager);
    d->removeItemFromCategory(itemId);
    if (!d->needAddIcon) {
        for (int i = 0; i < d->launcherModel.at(pageIndex)->count(); ++i) {
            if (d->launcherModel.at(pageIndex)->at(i)->id() == itemId) {
                removeAppIconFromTaskBar(d->launcherModel.at(pageIndex)->at(i)->desktopFile());
                d->takeAppIdFromDesktop(itemId, pageIndex);
                return;
            }
        }
        int groupId = d->itemGroupId(itemId);
        qDebug() << __func__ << groupId;
        removeItemFromGroup(itemId, groupId);
    }
}

void ModelManager::itemInstalled(int itemId,
                                 int indexOfAllCategory,
                                 int indexOfCategory,
                                 int page,
                                 bool newCategory,
                                 int category)
{
    Q_D(ModelManager);
    LauncherItem *launcherItem = d->createItem(itemId, page);
    if (!launcherItem) {
        return;
    }
    launcherItem->setState(d->globalState);

    // xc通用平板
    if (!d->needAddIcon && page != -1) {
        if (d->launcherModel.count() > page) {
            d->launcherModel.at(page)->push_back(launcherItem);
        } else if (d->launcherModel.count() == page) {
            PageModel *newPage = new PageModel;
            newPage->push_back(launcherItem);
            d->launcherModel.push_back(newPage);
        }
        updateCachedData(-1);
        return;
    }
    if (CategoryItem::All == d->currentCategoryOfAddion) {
        launcherItem->setState(LauncherItem::Selectable);
        launcherItem->setSelected(false);
    } else {
        launcherItem->setState(LauncherItem::Normal);
    }

    if (indexOfAllCategory < 0 || indexOfCategory < 0) {
        qWarning() << __func__ << "indexOfAllCategory or indexOfCategory is invalid !!!";
        return;
    }
    int insertAllCategoryPage = indexOfAllCategory / d->pageItemMaxOfAddition;
    int insertAllCategoryIndex = indexOfAllCategory % d->pageItemMaxOfAddition;

    // new All category page
    if (d->categoryModel.first()->pageCount() <= insertAllCategoryPage) {
        d->categoryItemsModel.insert(insertAllCategoryPage, new PageModel);
        d->categoryItemsModel.at(insertAllCategoryPage)->push_back(launcherItem);
        launcherItem->setPageIndex(insertAllCategoryPage);
        int newPageCount = d->categoryModel.first()->pageCount() + 1;
        d->categoryModel.first()->setPageCount(newPageCount);
        d->updateCategoriesItemsPageIndex(insertAllCategoryPage + 1, 1);
        Q_EMIT categoryPageAdded(CategoryItem::All);
    } else {
        d->categoryItemsModel.at(insertAllCategoryPage)->insert(insertAllCategoryIndex, launcherItem);
        launcherItem->setPageIndex(insertAllCategoryPage);
        int page = insertAllCategoryPage;
        while (d->categoryItemsModel.at(page)->count() > d->pageItemMaxOfAddition &&
               (page + 1) < d->categoryModel.first()->pageCount()) {
            auto item = d->categoryItemsModel.at(page)->takeLast();
            item->setPageIndex(item->pageIndex() + 1);
            d->categoryItemsModel.at(++page)->push_front(item);
        }
        if (d->categoryItemsModel.at(page)->count() > d->pageItemMaxOfAddition) {
            auto item = d->categoryItemsModel.at(page)->takeLast();
            d->categoryItemsModel.insert(++page, new PageModel);
            int newPageCount = d->categoryModel.first()->pageCount() + 1;
            d->categoryModel.first()->setPageCount(newPageCount);
            item->setPageIndex(item->pageIndex() + 1);
            d->categoryItemsModel.at(page)->push_front(item);
            d->updateCategoriesItemsPageIndex(page + 1, 1);
            Q_EMIT categoryPageAdded(CategoryItem::All);
        }
    }

    LauncherItem *categoryLauncherItem = new LauncherItem;
    *categoryLauncherItem = *launcherItem;
    if (d->currentCategoryOfAddion == category) {
        categoryLauncherItem->setState(LauncherItem::Selectable);
        categoryLauncherItem->setSelected(false);
    } else {
        categoryLauncherItem->setState(LauncherItem::Normal);
    }
    // create a new category
    if (newCategory) {
        int newPageIndex = 0;
        bool foundNewCategoryPos = false;

        CategoryItem *categoryItem = new CategoryItem;
        categoryItem->setCategory(category);
        QString categoryName = d_ptr->dataHandler->translatedName(AppCategoryManager::categoryToString(category));
        categoryItem->setName(categoryName);
        categoryItem->setPageCount(1);
        categoryItem->setNormalIcon(d->categoryIcon(categoryItem->category()).first);
        categoryItem->setClickIcon(d->categoryIcon(categoryItem->category()).second);

        for (int categoryIndex=0; categoryIndex<d->categoryModel.count() - 1; ++categoryIndex) {
            newPageIndex += d->categoryModel.at(categoryIndex)->pageCount();
            if (category > d->categoryModel.at(categoryIndex)->category() &&
                    category < d->categoryModel.at(categoryIndex + 1)->category()) {
                d->categoryModel.insert(categoryIndex + 1, categoryItem);
                foundNewCategoryPos = true;
                break;
            }
        }
        // the last category
        if (!foundNewCategoryPos) {
            d->categoryModel.push_back(categoryItem);
            ++ newPageIndex;
        }
        PageModel *newPage = new PageModel;
        categoryLauncherItem->setPageIndex(newPageIndex);
        newPage->push_back(categoryLauncherItem);
        d->categoryItemsModel.insert(newPageIndex, newPage);
        Q_EMIT categoryAdded(category);
        newPageIndex = foundNewCategoryPos ? newPageIndex + 1 : newPageIndex;
        d->updateCategoriesItemsPageIndex(newPageIndex, 1);
    } else {
        int insertCategoryPage = indexOfCategory / d->pageItemMaxOfAddition;
        int insertCategoryIndex = indexOfCategory % d->pageItemMaxOfAddition;
        int insertPage = insertCategoryPage;
        for (int index=0; index<d->categoryModel.count(); ++index) {
            if (d->categoryModel.at(index)->category() == category) {
                // insert to a new page
                if (d->categoryModel.at(index)->pageCount() <= insertCategoryPage) {
                    PageModel *pageModel = new PageModel;
                    pageModel->push_back(categoryLauncherItem);
                    if (d->categoryItemsModel.count() > insertPage) {
                        categoryLauncherItem->setPageIndex(insertPage);
                        d->categoryItemsModel.insert(insertPage, pageModel);
                        d->updateCategoriesItemsPageIndex(insertPage + 1, 1);
                    } else {
                        categoryLauncherItem->setPageIndex(d->categoryItemsModel.count());
                        d->categoryItemsModel.push_back(pageModel);
                    }
                    Q_EMIT categoryPageAdded(d->categoryModel.at(index)->category());
                }
                // insert to a already page
                else {
                    categoryLauncherItem->setPageIndex(insertPage);
                    d->categoryItemsModel.at(insertPage)->insert(insertCategoryIndex, categoryLauncherItem);
                    do {
                        if (d->categoryItemsModel.at(insertPage)->count() > d->pageItemMaxOfAddition) {
                            auto item = d->categoryItemsModel.at(insertPage)->takeLast();
                            item->setPageIndex(item->pageIndex() + 1);
                            // the last page of this category
                            if (d->categoryModel.at(index)->pageCount() == insertCategoryPage + 1) {
                                PageModel *pageModel = new PageModel;
                                pageModel->push_back(item);
                                d->categoryItemsModel.insert(insertPage + 1, pageModel);
                                d->updateCategoriesItemsPageIndex(insertPage + 2, 1);
                                d->categoryModel.at(index)->setPageCount(d->categoryModel.at(index)->pageCount() + 1);
                                Q_EMIT categoryPageAdded(d->categoryModel.at(index)->category());
                                break;
                            } else {
                                d->categoryItemsModel.at(insertPage ++)->push_front(item);
                            }
                        }
                    } while(insertPage < d->categoryModel.at(index)->pageCount());
                }
                break;
            }
            insertPage += d->categoryModel.at(index)->pageCount();
        }
    }
}

void ModelManager::iconThemeChanged()
{
    Q_D(ModelManager);
    auto itemsData = d->dataHandler->items();
    auto updateIconFunction = [&itemsData](LauncherItem *item, int itemId) {
        UKUI::IconItem *iconItem =
                static_cast<UKUI::IconItem *>(itemsData.value(itemId));
        if (iconItem) {
            item->setIcon(iconItem->getIconName());
        } else {
            qWarning() << "static_cast to UKUI::IconItem * failed: "
                       << item->id();
        }
    };

    auto groupIt = d->groupModel.begin();
    while (groupIt != d->groupModel.end()) {
        d->iterateMultiPageModelWithItemCallBack(groupIt.value(), [updateIconFunction, d](LauncherItem *item) {
            if (item->id() != 0 && item->type() == LauncherItem::AppIcon) {
                updateIconFunction(item, item->id());
                LauncherItem *windowOpenItem = d->taskbarWindowOpenItem(item->desktopFile());
                if (windowOpenItem != nullptr) {
                    updateIconFunction(windowOpenItem, item->id());
                }
            }
        });
        ++ groupIt;
    }

    if (!d->needAddIcon) {
        d->iterateMultiPageModelWithItemCallBack(&d->launcherModel, [updateIconFunction, d](LauncherItem *item) {
            if (item->type() == LauncherItem::AppIcon) {
                updateIconFunction(item, item->id());
                LauncherItem *windowOpenItem = d->taskbarWindowOpenItem(item->desktopFile());
                if (windowOpenItem != nullptr) {
                    updateIconFunction(windowOpenItem, item->id());
                }
            }
        });
    } else {
        auto categoryIt = d->categoryItemsModel.begin();
        while (categoryIt != d->categoryItemsModel.end()) {
            for (auto item : (**categoryIt)) {
                updateIconFunction(item, item->id());
            }
            ++ categoryIt;
        }
    }
}

void ModelManager::changeAppCategory(const quint32 itemId, const QList<int> newCategory)
{
    Q_D(ModelManager);
    if (itemId < 0) {
        return;
    }
    for (const auto &pageItems : d->launcherModel) {
        for (const auto &item : *pageItems) {
            if (item->id() == itemId) {
                item->setCategories(newCategory);
                return;
            }
        }
    }

    auto groupModelIt = d->groupModel.constBegin();
    while (groupModelIt != d->groupModel.constEnd()) {
        auto groupModel = groupModelIt.value();
        for (int page=0; page<groupModel->count(); ++page) {
            for (int itemIndex=0; itemIndex<groupModel->at(page)->count(); ++itemIndex) {
                if (groupModel->at(page)->at(itemIndex)->id() == itemId &&
                        groupModel->at(page)->at(itemIndex)->type() == LauncherItem::AppIcon) {
                    groupModel->at(page)->at(itemIndex)->setCategories(newCategory);
                    return;
                }
            }
        }
        ++ groupModelIt;
    }
}

void ModelManager::onTaskbarAppIdPosExchanged(int fromIndex, int toIndex)
{
    Q_D(ModelManager);
    if (!d->itemIndexIsValid(fromIndex, 0, d->groupModel.value(d->kTaskBarGroupId)) ||
            !d->itemIndexIsValid(toIndex, 0, d->groupModel.value(d->kTaskBarGroupId))) {
        qWarning() << __func__ << "taskbar app exchange position failed !!!" << fromIndex << toIndex;
        return;
    }
    int findFromIndex = d->itemIndexWithPinnedToTaskBar(fromIndex);
    int findToIndex = d->itemIndexWithPinnedToTaskBar(toIndex);
    if (!d->itemIndexIsValid(findFromIndex, 0, d->groupModel.value(d->kTaskBarGroupId)) ||
            !d->itemIndexIsValid(findToIndex, 0, d->groupModel.value(d->kTaskBarGroupId))) {
        qWarning() << __func__ << "taskbar app exchange position failed !!!" << findFromIndex << findToIndex;
        return;
    }
    d->groupModel.value(d->kTaskBarGroupId)->at(0)->move(findFromIndex, findToIndex);
    qDebug() << __func__ << findFromIndex << findToIndex;
}

void ModelManager::onItemIdMovedToTaskbar(const int itemId, const int insertPosIndex)
{
    Q_D(ModelManager);
    if (!d->itemIndexOfInsertTaskbarIsValid(insertPosIndex)) {
        qWarning() << __func__ << "add itemId to group failed !!!" << insertPosIndex;
        return;
    }

    int findInsertPos = d->itemIndexWithPinnedToTaskBar(insertPosIndex);
    qDebug() << "model insertPosIndex" << findInsertPos;
    if (!d->itemIndexOfInsertTaskbarIsValid(findInsertPos)) {
        qWarning() << __func__ << "add itemId to group failed !!!" << findInsertPos;
        return;
    }

    UKUI::IconItem *iconItem =
            static_cast<UKUI::IconItem *>(d->dataHandler->items().value(itemId));
    LauncherItem *launcherItem = d->createLauncherItem(LauncherItem::AppIcon, iconItem, 0);
    d->groupModel.value(d->kTaskBarGroupId)->at(0)->insert(findInsertPos, launcherItem);

    for (int j = 0; j < d->groupModel.value(d->kTaskBarGroupId)->at(0)->count(); ++j) {
        if (d->groupModel.value(d->kTaskBarGroupId)->at(0)->at(j)->type() == LauncherItem::WindowIcon &&
                d->groupModel.value(d->kTaskBarGroupId)->at(0)->at(j)->desktopFile() == launcherItem->desktopFile()) {
            d->groupModel.value(d->kTaskBarGroupId)->at(0)->removeAt(j);
            launcherItem->setAppOpen(true);
        }
    }
}

void ModelManager::onItemIdTakedFromTaskbar(const int itemId)
{
    Q_D(ModelManager);
    auto item = d->item(itemId);
    if (item == nullptr) {
        qWarning() << __func__ << "itemId not found !!!";
        return;
    }
    if (item->appOpen()) {
        item->setId(d->availableId());
        item->setType(LauncherItem::WindowIcon);
    } else {
        d->groupModel.value(d->kTaskBarGroupId)->at(0)->removeOne(item);
    }
}

void ModelManager::onItemIdTakedFromDesktop(const int itemId, const int pageIndex)
{
    Q_D(ModelManager);
    d->takeAppIdFromDesktop(itemId, pageIndex);
}

void ModelManager::onItemIdTakedFromGroup(int itemId, int groupId)
{
    Q_D(ModelManager);
    d->takeAppIdFromGroup(itemId, groupId);
}
